package com.xnx3.weixin.weixinPay.v2;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;

import com.xnx3.BaseVO;
import com.xnx3.Lang;
import com.xnx3.weixin.SignUtil;
import com.xnx3.weixin.bean.MchPayBean;
import com.xnx3.weixin.util.WXPayRequest;

/**
 * 微信商户付款至用户零钱
 * https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_1
 * @author 刘晓腾
 */
public class MchPay{

	/**
	 * 请求URL
	 */
	public static final String REQUEST_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
	
	/**
	 * FORCE_CHECK：强校验真实姓名，此时收款用户姓名为必填
	 */
	public static final String CHECK_NAME_YES = "FORCE_CHECK";
	/**
	 * NO_CHECK：不校验真实姓名，此时收款用户姓名可不填
	 */
	public static final String CHECK_NAME_NO = "NO_CHECK";

	
	static Pay pay;	//微信付款到零钱
	static {
		pay = new Pay();
		//微信支付的商户号，格式如：1581125900 ，登录 https://pay.weixin.qq.com/ 取得
		pay.mch_id = "1616291102";	
		//微信支付的API密钥，32位字符串。登录 https://pay.weixin.qq.com/ 在 [账户中心] - [API安全] - [设置API密钥]
		pay.mch_key = "Banmahuishou963258banxmagongzhon";	
		//微信小程序的appid，格式如 wx07f3db3a6bbexxxx
		pay.gongzhonghao_appid = "wx7dd2743e744baac9";
	}
	
	/**
	 * 发起支付到零钱的订单，不校验真实姓名
	 * @param partnerTradeNo 商户订单号，需保持唯一性，必填
	 * @param openid 用户openid，必填
	 * @param amount 付款金额，必填（单位：分）
	 * @param desc 付款备注，必填
	 * @param deviceInfo 设备号，选填
	 * @return
	 */
	public BaseVO createMchPayOrder(String partnerTradeNo, String openid, 
			String amount, String desc, String deviceInfo) {
		
		return createMchPayOrder(partnerTradeNo, openid, amount, desc, deviceInfo, CHECK_NAME_NO, "");
	}
	
	/**
	 * 发起支付到零钱的订单
	 * @param partnerTradeNo 商户订单号，需保持唯一性，必填
	 * @param openid 用户openid，必填
	 * @param amount 付款金额，必填（单位：分）
	 * @param desc 付款备注，必填
	 * @param deviceInfo 设备号，选填
	 * @param checkName 是否校验收款人姓名（NO_CHECK：不校验真实姓名，FORCE_CHECK：强校验真实姓名）
	 * @param reUserName 收款人真实姓名（如果checkName设置为FORCE_CHECK，则必填用户真实姓名。如需电子回单，需要传入收款用户姓名）
	 * @return
	 */
	public BaseVO createMchPayOrder(String partnerTradeNo, String openid,
			String amount, String desc, String deviceInfo, String checkName, String reUserName) {
		MchPayBean bean = new MchPayBean();
		
		bean.setPartnerTradeNo(partnerTradeNo);
		bean.setOpenid(openid);
		bean.setCheckName(checkName);
		bean.setReUserName(reUserName);
		bean.setAmount(amount);
		bean.setDesc(desc);
		bean.setDeviceInfo(deviceInfo);
		
		return createMchPayOrder(bean);
	}
	
	/**
	 * 发起支付到零钱的订单
	 * @param MchPayBean 所需参数的bean
	 * @return
	 */
	public BaseVO createMchPayOrder(MchPayBean bean) {
		
		// 创建Map，用来计算签名
		SortedMap<String, String> paramMap = new TreeMap<String, String>();
		/*
		 * 记录请求参数
		 */
		// 商户账号appid
		paramMap.put("mch_appid", pay.gongzhonghao_appid);
		// 商户号
		paramMap.put("mchid", pay.mch_id);
		// 商户订单号
		paramMap.put("partner_trade_no", bean.getPartnerTradeNo());
		// 用户openid
		paramMap.put("openid", bean.getOpenid());
		// 校验用户姓名选项
		paramMap.put("check_name", bean.getCheckName());
		// 收款用户姓名
		if ("FORCE_CHECK".equals(bean.getCheckName())) {
			paramMap.put("re_user_name", bean.getReUserName());
		}
		// 金额，单位为分
		paramMap.put("amount", bean.getAmount());
		// 付款备注
		paramMap.put("desc", bean.getDesc());
		// 设备号
		if (bean.getDeviceInfo() != null && !"".equals(bean.getDeviceInfo())) {
			paramMap.put("device_info", bean.getDeviceInfo());
		}
		// 设置随机字符串
        String nonceStr = Lang.uuid();
        paramMap.put("nonce_str", nonceStr);
		
		/* 
		 * 计算签名
		 */
		String sign = SignUtil.generateSign(paramMap, pay.mch_key);
		
		/*
		 * 将参数 编写XML格式
		 */
		StringBuffer paramBuffer = new StringBuffer();
		paramBuffer.append("<xml>");
		paramBuffer.append("<amount>" + paramMap.get("amount") + "</amount>");
		paramBuffer.append("<check_name>" + paramMap.get("check_name") + "</check_name>");
		paramBuffer.append("<desc>" + paramMap.get("desc") + "</desc>");
		paramBuffer.append("<mch_appid>" + pay.gongzhonghao_appid + "</mch_appid>");
		paramBuffer.append("<mchid>" + pay.mch_id + "</mchid>");
		paramBuffer.append("<nonce_str>" + paramMap.get("nonce_str") + "</nonce_str>");
		paramBuffer.append("<openid>" + paramMap.get("openid") + "</openid>");
		paramBuffer.append("<partner_trade_no>" + paramMap.get("partner_trade_no") + "</partner_trade_no>");
		if ("FORCE_CHECK".equals(bean.getCheckName())) {
			paramBuffer.append("<re_user_name>" + paramMap.get("re_user_name") + "</re_user_name>");
		}
		if (bean.getDeviceInfo() != null && !"".equals(bean.getDeviceInfo())) {
			paramBuffer.append("<device_info>" + paramMap.get("device_info") + "</device_info>");
		}
		paramBuffer.append("<sign>" + sign + "</sign>");
		paramBuffer.append("</xml>");
		WXPayRequest wxPayRequest = new WXPayRequest();
		String resultXml = "";
		Map<String, String> result = null;
		// 获取证书路径（部到测试服务器显示找不到这个文件 所以注释掉了换成下面的路径）
//		String wxCert = getClass().getResource("/static/CSR_WX/wxPay/apiclient_cert.p12").getFile();
		try {
			// TODO 证书路径需要自己配置
			resultXml = wxPayRequest.requestWithCert(REQUEST_URL, 
					nonceStr, 
					paramBuffer.toString(), 
					pay.mch_id, 
					"/mnt/cert/apiclient_cert.p12");
			result = doXMLParse(resultXml);
			// 微信支付到零钱请求失败
			if (result != null && 
					("FAIL".equals(result.get("return_code")) || "FAIL".equals(result.get("result_code")))) {
				System.out.println("微信支付到零钱请求失败：" + resultXml);
				System.out.println("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
				return BaseVO.failure("微信支付到零钱请求失败");
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			System.out.println("微信支付到零钱请求失败");
			System.out.println(e.toString());
			System.out.println("微信支付到零钱请求失败：" + e.toString());
			System.out.println("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
			return BaseVO.failure("微信支付到零钱请求失败");
		}
		
		System.out.println("微信支付到零钱请求成功：" + resultXml);
		System.out.println("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
		
		
		return BaseVO.success(resultXml);
	}
	
	public static String callback() {
		return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
	}
	
	/**
	 * 解析xml,返回第一级元素键值对。如果第一级元素有子节点，则此节点的值是子节点的xml数据。
	 * @param strxml
	 * @throws Exception
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public static Map<String, String> doXMLParse(String strxml) throws Exception {
		if (null == strxml || "".equals(strxml)) {
			return null;
		}

		Map<String, String> m = new HashMap<String, String>();
		InputStream in = String2Inputstream(strxml);
		SAXBuilder builder = new SAXBuilder();
		Document doc = builder.build(in);
		Element root = doc.getRootElement();
		List list = root.getChildren();
		Iterator it = list.iterator();
		while (it.hasNext()) {
			Element e = (Element) it.next();
			String k = e.getName();
			String v = "";
			List children = e.getChildren();
			if (children.isEmpty()) {
				v = e.getTextNormalize();
			} else {
				v = getChildrenText(children);
			}

			m.put(k, v);
		}

		// 关闭流
		in.close();

		return m;
	}
	
	public static InputStream String2Inputstream(String str) {
		return new ByteArrayInputStream(str.getBytes());
	}
	
	/**
	 * 获取子结点的xml
	 * @param children
	 * @return String
	 */
	@SuppressWarnings("rawtypes")
	public static String getChildrenText(List children) {
		StringBuffer sb = new StringBuffer();
		if (!children.isEmpty()) {
			Iterator it = children.iterator();
			while (it.hasNext()) {
				Element e = (Element) it.next();
				String name = e.getName();
				String value = e.getTextNormalize();
				List list = e.getChildren();
				sb.append("<" + name + ">");
				if (!list.isEmpty()) {
					sb.append(getChildrenText(list));
				}
				sb.append(value);
				sb.append("</" + name + ">");
			}
		}

		return sb.toString();
	}
	
	/**
	 * 对所有传入参数按照字段名的 ASCII 码从小到大排序（字典序），并且生成url参数串
	 * @param paraMap  要排序的Map对象
	 * @param urlEncode  是否需要URLENCODE
	 * @param keyToLower  是否需要将Key转换为全小写 true:key转化成小写，false:不转化
	 * @return String
	 */
	public static String formatUrlMap(Map<String, String> paraMap, boolean urlEncode, boolean keyToLower) {
		String buff = "";
		Map<String, String> tmpMap = paraMap;
		try {
			List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(tmpMap.entrySet());
			// 对所有传入参数按照字段名的 ASCII 码从小到大排序（字典序）
			Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {
				@Override
				public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
					return (o1.getKey()).toString().compareTo(o2.getKey());
				}
			});
			// 构造URL 键值对的格式
			StringBuilder buf = new StringBuilder();
			for (Map.Entry<String, String> item : infoIds) {

				if (item.getKey() != null && item.getKey().length() > 0) {
					String key = item.getKey();
					String val = item.getValue();
					if (urlEncode) {
						val = URLEncoder.encode(val, "utf-8");
					}
					if (keyToLower) {
						buf.append(key.toLowerCase() + "=" + val);
					} else {
						buf.append(key + "=" + val);
					}
					buf.append("&");
				}

			}
			buff = buf.toString();
			if (buff.isEmpty() == false) {
				buff = buff.substring(0, buff.length() - 1);
			}
		} catch (Exception e) {
			return null;
		}
		return buff;
	}
	
}
